<?php

namespace support;

use FastRoute\Dispatcher;
use FastRoute\RouteCollector;
use InvalidArgumentException;
use ReflectionClass;

use function FastRoute\simpleDispatcher;

class Route
{
    private $dispatcher;

    public function __construct()
    {
        $list = $this->load();
        $this->dispatcher = simpleDispatcher($this->make($list));
    }

    public function load()
    {
        $list = [];
        $routeFiles = glob(root_path() . 'app/*/route/*.php');
        foreach ($routeFiles as $file) {
            $list[] = require_once($file);
        }
        return $list;
    }

    public function make($list)
    {
        return function (RouteCollector $router) use ($list) {
            foreach ($list as $item) {
                if (isset($item['prefix'])) {
                    $router->addGroup($item['prefix'],  $this->make($item['group']));
                } else {
                    $router->addRoute(strtoupper($item[0]), $item[1], $item[2]);
                }
            }
        };
    }

    public function handle($handler, $vars)
    {
        [$className, $methodName] = explode('/', $handler);
        $constructorArgs = [];

        // 1. 反射获取类
        $className = str_replace(['/', '.'], '\\', $className);
        $classReflection = new ReflectionClass($className);
        // 2. 实例化对象 (支持带参数的构造函数)
        $instance = $classReflection->newInstanceArgs($constructorArgs);
        // 3. 反射获取方法
        $methodReflection = $classReflection->getMethod($methodName);
        // 4. 获取方法的参数信息
        $methodParams = $methodReflection->getParameters();
        // 5. 构建要传递给方法的参数值数组
        $args = [];
        // 默认注入request
        $vars['request'] = request();
        foreach ($methodParams as $param) {
            $paramName = $param->getName();
            if ($paramName === 'request') {
                $args[] = request();
            } elseif (array_key_exists($paramName, $vars)) { // 检查 $vars 中是否存在对应参数名的键
                $args[] = $vars[$paramName];
            } elseif ($param->isDefaultValueAvailable()) {
                // 如果参数有默认值且 $dict 中没有提供，则使用默认值
                $args[] = $param->getDefaultValue();
            } else {
                // 既没有提供值，也没有默认值，抛出异常
                throw new InvalidArgumentException("Missing required parameter '$paramName' for method '$methodName' in class '$className'.");
            }
        }
        // 6. 调用方法并返回结果
        return $methodReflection->invokeArgs($instance, $args);
    }

    public function dispatch()
    {
        $httpMethod = $_SERVER['REQUEST_METHOD'];
        $uri = $_SERVER['REQUEST_URI'];

        if (false !== $pos = strpos($uri, '?')) {
            $uri = substr($uri, 0, $pos);
        }
        $uri = rawurldecode($uri);

        $routeInfo = $this->dispatcher->dispatch($httpMethod, $uri);

        switch ($routeInfo[0]) {
            case Dispatcher::NOT_FOUND:
                $this->handleNotFound();
                break;
            case Dispatcher::METHOD_NOT_ALLOWED:
                $allowedMethods = $routeInfo[1];
                $this->handleMethodNotAllowed($allowedMethods);
                break;
            case Dispatcher::FOUND:
                $handler = $routeInfo[1];
                $vars = $routeInfo[2];
                return $this->handle($handler, $vars);
                break;
        }
    }

    private function handleNotFound()
    {
        http_response_code(404);
    }

    private function handleMethodNotAllowed($allowedMethods)
    {
        http_response_code(405);
        header('Allow: ' . implode(', ', $allowedMethods));
    }
}
